209 IX: IX Family of Software: requires() Phase I (209.html)

Keywords

ICH180RR requires ISO9003 import prepIX_I.py show.py "version control" v ixv "function programmer" "user programmer" _v03 isShow "IX: PrepIX" ISO9003_Software_Version_Log.txt "LIST OF REQUIRED IX FUNCTION VERSIONS" Functions Macros Entities "Repositories of Functions" OOP Object-Oriented-Programs object module "Source code" "main program" Cytron "CM4 Maker board" "Compute Module 4" CM4 CM4008016 Repository CM4-IO-BASE-A &ix| nShowDefs showIt showIt_ix.py prepIn.py lexicon show prepIX_I

/KeywordsEnd

(To enlarge .....Click it)
thumb: IXimage.jpg
IX or (IX by DC) or "|><"


This article is a part of the IX family of software.

Introduction

The "requires" function is an integral part of the IX Software Family. More is said about the whole IX Software Family in Source 1 below.

	The requires() function in the IX family is a very powerful component.  Due
	to its complexity, it is being implemented in 2 Phases. This article describes 
	Phase I of the requires(func(. . . . ) as implemented by prepIX_I in the IX family.
	The deprecated program prepIX is being replaced by prepIX_I.

	The prepIX_I program has not yet been fully coded as of 2023KNov025 The latest article 
	describing an early version of prepIX is article 196 (Source 10) dated 2023 E May 16.  
	Watch this article (209) for the release of prepIX_I which will include phase I of 
	"requires".
	
After describing the "requires" function of prepIX, the difference between functions, entities and macros will be explained. These related concepts are important in the IX Family of Software.

A useful IX function for debugging, named show() [version 0] and a useful IX macro definition named showIt_ix.py will both be fully explained and used as an example in this article.

Finally, repositories will be introduced. Repositories are locations (such as libraries, folders and packages) where functions (and other Python modules) can be found; within the source code of a program or stored, internally in a computer or externally on the web. Python repositories are endless!

If the reader encounters words or phrases that need to be explained, refer to the "lexicon" in article 196 (Source 10).

The "requires()" function (in prepIX_I)

The "requires" function is a very powerful extension of the standard "import" statement that is provided by Python. Requires will be introduced in 2 phases. In Phase I, the improved features of requires (over import) are:

	1) "requires" adds a function to a main program by prepending visible numbered statements.
	2) "requires" can be told to look for functions in packages external to the current folder.
	3) prepIX_I lists the versions of functions that could be found by the "requires" function.
		See the #****LIST OF REQUIRED IX FUNCTION VERSIONS**** suffixed to the 
		resulting fiinal program.
	4) "requires" in Phase II can very selectively enforce the version control begun in Phase I. 
		(Python's standard "import" lacks all version control of functions).
	
	The author will implement the "requires" functionality in 
	two distinct levels, steps or phases when releasing prepIX,  
	to minimize the steepness of the complexity of "requires".

	In Phase I, "requires" will only accept "v==0" of functions and will 
	only accept the definition of "requires" as built-into prepIX_I.
	
In Phase II (Source 02), "requires" can be modified by users of the IX family of Software, although this is not recommended. Furthermore, in Phase II, the "requires" function can be told to use alternate versions of "requires". Also in Phase II, the version of each function that is inserted into a main program by "requires" can be a version higher than 0 and requires can be very selective about the exact version (or range of versions) that are required for each function. Many extensions of the functionality of "requires" have been intentionally delayed until Phase II of "requires". The intention is that the concept of "requires" and its syntax are introduced in Phase I of the prepIX module, known as prepIX_I. Some programmers will be content with the effectiveness of Phase I of "requires" and will never use the features in Phase II. Programmers who are more senior, can gradually use more and more of the features of Phase II of "requires". Phase II of "requires" is described in detail in Source 02.

Functions, Entities and Macros will be contrasted later in this article, but the "requires" function will be explained first. Note that all macros will be processed (by IXp) before functions are added (as "required") by prepIX_I.

The "requires" Function

When using the "requires" function itself, the program needs the following import statement to be used. The "requires.py" module should be included in the same folder as the main program that is being created.

	from requires import requires
	
Of course, the "requires" function, itself, cannot simply be "required" by a "requires" statement.

The "requires" function has (at least) the following 4 main purposes or uses:

A. Import-like Purpose

The running of the "requires" function will do a similar job to that of an import statement except that the requested function definition statements will be automatically inserted in front of the main program. This will be automatically done by the prepIX_I.py program. The "requires" function (formerly known as the deprecated "requests" function) should specify that version 0 of the "required" function will be used. The use of higher numbered versions of functions is not recommended in Phase I.

B. Function Availability

Proper use of the "requires" function causes prepIX_I to verify that version 0 of the function that is being required is available. An error condition will be raised or a warning issued if the function is not available. When searching for the function, the prepIX_I program will look in the current folder and in the Desktop/IX_assets/ix/ix_pkg.py file. The "ix_pkg.py" file is a package containing all the functions being used by the IX family of software. Users of the IX family of software can also add functions to this package, if they wish. The "textpack.py" IX program (in Source 11) can be used to add functions to this package.

Function Pairs in the IX Family of Software

Every function requested using the "requires" process is actually a function pair. For example, in the IX family, a Python function named "show(s,s,s)" needs two related show functions for "requires" to work. They will be show(s,s,s) and show_v00(s,s,s). Note that the version number (i.e. 0) of the most current show function appears as a suffix to the name "show" in the name of the second function of the function pair. This name is "show_v00". This syntax allows for future version numbers up to 99 to be used. But note that Phase I only supports use of version 0, which is a suffix of "_v00". The simple show(s,s,s) function is what is normally coded by the programmer so that a Python program will invoke the function "show". The show(s,s,s) function merely calls the show_v00(s,s,s) function, which is the current version of the show function. This function-pair permits the programmer to use the show(s,s,s) function without needing to know the current version of the required function. The actual work (or algorithm of the show function) is done by the show_v00(s,s,s) function. The IX family of software will use a similar function-pair whenever "requires" is used. This tells prepIX_I that an external function is needed. As with normal Python coding, in-line functions can still be used with the IX family of software. Instead of coding "from show import show", the user of the IX family of software will code:

	requires("show(s,s,s,'v==0')")
	
The IX family of software needs to know how many parameters "show" will need AND the version of "show" that is required. Note that the version number must be the integer 0 not a floating point 0.0 . Furthermore the 0 following the "==" must NOT be the the string '0' nor "0" which would be enclosed in a pair of single or double quotes. The single quotes enclosing the 'v==0' are needed. The single parameter given to the requires function is a string and must be enclosed in double quotes, as shown in the examples in this article. The "requires" function demands that this exact syntax be used.

In the development of software, a number of different versions of a function are often generated over the lifetime of a function, but not at the beginning. In the IX Family of software, in Phase II of "requires", each version of a function should be maintained in THREE distinct places, as described below. However Phase I of "requires" expects version 0 to be used, so version control can be easily introduced. Phase I expects that other versions will be avoided. Therefore there is no need to maintain complete version control in Phase I. Much better knowledge of version control is required if Phase II of "requires" is ever used. It is helpful to have a brief preview of the future Version Maintenance in Phase II, as described next. The neophyte programmer might skip the following paragraphs until heading "D".

In Phase II, Version Maintenance will be done in three "places":

1) the actual new version of the module. The version number must be part of the name of the module. In this example, the name of version 0 must be show_v00.py. The code in version 0 of the module will certainly differ from the code in version 1, the next version.

2) no version # in the function name called. the function actually called by the main program must NOT have the version number embedded in its name. But the version number must appear within the function ( ie in show.py). But the user programmer doesn't need to worry about specifying a version # in any subsequent code invoking show() that he/she writes. See the example below:
	isShow = True
	firstName = "Henry"
	show("firstName", firstName, isShow)
For each function used by the program, it is only the requires() statement that is located near the top of the main program that needs to know the version of show for the main program. This "requires" version number" must NOT appear every subsequent time show() is invoked in the main program. It is only the "requires()" statement that needs to know the version number. The "requires()" doesn't need to know the actual parameters that will be used. This is because "requires" is NOT really used to invoke show(), but it needs to know the number of parameters that show() uses. For this reason, the actual format (syntax) of the "requires()" statement for the "show()" function (near the top of the main program) in Phase I is:
	requires("show(s,s,s,'v==0')")
	
That is really all that the user programmer needs to do (instead of coding an "import" statement. Later in the main program, he/she can code the following to invoke show without mentioning a version number. As can be seen, this is a very normal invocation of the show() function, normal in that it does not mention the version of the show() function that will be used.
	isShow = True
	firstName = "Henry"
	show("firstName", firstName, isShow)
	
Of course, this way, the exact same version of show (provided to "requires") will be used each time show is invoked in one program.

3) ISO9003 - version control. The version control needed by the ISO9003 standard is described next.

C. ISO9003 Software Version Log

The reasons may be quite complex to understand, but (future) version control of software must not be taken lightly. If this is unclear, get assistance to learn more about good version control practices. Unfortunately, most communities using Python do NOT pay enough attention to software version control. Version control is so important that the world's best corporate ISO standards demand that it be addressed, be given priority and be maintained as shown next.

Therefore, the third place where the version number of software must be maintained is in the "ISO9003_Software_Version_Log.txt" file. A prerelease sample copy by the author for the IX Software Family can be seen in Source 08. Such version control is only absolutely necessary when using Phase II of "requires".

(simplified)ISO9003 Software Version Log
*************************************************************************
Software module
Name		Vsn Parms Hash	  Date(YMD)	      Description
----------	-     -----    ----	  ------------             -----------------------------------------
. . . .
show.py		0    3         N      2023KNov25	      function (debug) used to display 
							 the value of a variable.
inputWto.py	na  2	    N	  2023AJan01	      function used to request input 
					  		With a waiting time-out
. . . .

Latest version of ISO9003_Software_Version.txt is version 2 dated 2023KNov25

/ISO9003_Software_Version_Log.txt
*************************************************************************
Now, returning back to the use of the prepIX_I.py program with Phase I of "requires".

D. Version list of required functions used in the main program

The prepIX_I program will also generate a list of the required functions and their actual versions. This list appears as comments appended to the bottom of the source code generated by prepIX. This list does NOT include the names of any macros used, only functions. This list might be:

	# ****LIST OF REQUIRED IX FUNCTION VERSIONS********
	# Highest	Loaded    Required             MD5
	# Vsn Avail.	Vsn 	  Vsn                  Hash
	# ----		----	  -------------------- ----
	# v00		v00	  show(s,s,s,'v==0')   Pass
	# none		none	  inputWto(s,s,'v==0') none
	# *************************************************
Note that a "none" appears in the column(s) if a "required" function-pair cannot be found/used.

The 'v==0' causes prepIX_I to almost completely ignore automatic version control. In Phase I, each "requires" statement encountered will have the form of:

	requires("func(s. . 'v==0')") 
	
Each such statement is converted into 3 statements that will do a similar action as the first 3 statements shown below:
	from ix_pkg import show
	from ix_pkg import show_v00
	requires("show(s,s,s, 'v==0')")
	. . . . .
	isShow=True
	firstName="Henry"
	show("firstName",firstName,isShow)
	. . . . .

	This will prepend the function-pair (found in ix_pkg.py) to the main program 
	instead of importing it normally. (The normal imported statements are NOT visible).
	
As can be seen, the "requires" function and the prepIX_I program are used together as one of the most important components of the IX family of software. The user programmer must code the "requires" statement and the subsequent "show" statement(s). However, in Phase I, the "requires" function can sometimes be completely disregarded (unused), if the user programmer wishes to produce shorter less complex Python programs. Both "requires" and "import" can be used together, but this can often lead to confusion.

The advanced version of prepIX in phase 2 is able to verify the MD5 hash total of each module that is required. Verification of an MD5 Hash total will result in one of three verdicts: "Pass", "Fail" or "none". The verdict (or result) will appear in the LIST OF REQUIRED IX FUNCTION VERSIONS (shown above). The prepIX_I.py module will only show a result of "none" regardless of the presence or absence of an MD5 hash total in the source code for the function that the code "requires".

Written Code vs Generated Code

A Python program is made up of a list of statements commonly referred to as "code". Code is run (executed) on a computer. The path taken by the computer when executing code is called a thread. The thread always begins at the top of the code. The thread stops when an exit() function is encountered or when the thread reaches the bottom of the code. Sometimes the thread encounters an "endless loop". Execution continues forever in an endless loop, until the value of a variable changes to make it encounter an exit() function. Other ways to stop a program are to hit ctrl-c on the keyboard or to shut-down the Operating System or to Turn Off the Power to the computer.

Most code is written by a programmer (not automatically generated). Each program is usually unique. Because of this, the programmer must write most of the statements, at least those that are in the main module.

Some code in a program can be automatically generated or inserted into the main module without the programmer coding it line-by-line. There are three types of generated code:

Functions, Entities and Macros

FUNCTIONS are COMPLETE FUNCTION DEFINITIONS beginning with a "def" statement and followed by a function name. It is also possible to import a function before it is referenced. The statements that comprise imported functions are not usually visible in a listing of the main program. Each function definition or import statement must be physically located in the main module in front of (physically preceding) any statements that reference the function. Usually such a reference actually invokes the function. A function can be a routine that returns one or more pieces of information, but it is not imperative that a function return any value or anything at all. Functions not returning anything are often called subroutines. Functions usually terminate their execution of a thread with a "return" statement that returns run-time control (or the thread) to the program that invoked the function. Each time a function is invoked, the exact same set of statements in the function are used, however each thread through a function might take a different path through the function. A program execution thread usually travels completely through a function although sometimes threads can be created or interrupted. A function is presented with zero or more input parameters each time it is called. The values of these parameters can often be different each time the function is called (invoked). The actions taken by a function are determined by these input parameters and by other information that the function encounters or gathers during its execution.

ENTITIES are usually assigned a value by the programmer before the macro modules are invoked (converted into code.) (A macro is defined in more detail in the next paragraph.) A macro can be cited in various different instances (even multiple times) in a program. Often each instance is different. It is the different value of the entities used by a macro that cause the macro to generate different code. In IX macros, the use of an entity appears in one macro definition (or place in the code) and also appears in another macro invocation or place. Sometimes an entity is not defined at all, but an attempt is made to use it; this is seen as an error and causes an error to be thrown during the evaluation phase of the macro preprocessor, although the IX software could be changed to simply consider an undefined entity as a null string. On any code line that invokes a macro, most of the entities used by that macro will be defined or will have already been defined by the user programmer. In the current IX Software Family, all entities must be predefined by a macro or by the programmer before they can be used to generate code in a macro. Often, the macro invocation is immediately followed by a "?" and the entity values. In IX macros, entities can be defined in one macro and used in that macro or in another macro or never used at all. Some macro systems set all entity values to "undefined" at the end of each macro generation instance, IX macros don't do this. In fact, an IX macro might only assign values to entities and might not generate any code at all. All entity values are forgotten immediately after the last macro is used to generate code. Entities have no value, use or meaning, once the finished program has begun to run. In IX macros, the format of an entity name is "&item|". Every entity must begin with "&" and a lower case alphabetic character, then any alphanumeric characters (and a few others eg "_" and "-") are allowed, finally ending with a solid vertical bar ("|") as the last character of the entity name. Programmers using IX macros should avoid defining entity names that begin with "&ix_" because such names are reserved for use by the IX macro system itself.

MACROS generate one or more lines of code before the program begins to run. Such lines of code are defined in a macro definition module. Some people say that macros must be used by a pre-processor before the final program generation is complete. The lines of macro code generated by the preprocessor do NOT normally begin with a "def" and do not normally contain a "return" statement. Some macros only generate data, variables or entities, in which case an execution thread might not be understood to travel through a macro. A single macro might generate vastly different code each time it is invoked (used to generate code). In the IX family, a macro always generates full lines of code, not partial lines of code. At code generation time (before run time), an invoked macro can be presented with zero, one or more entity value definitions. Each entity is usually a small string of characters (or a number) whose value usually remains constant during the evaluation phase of the macro. An entity was defined in more detail in the previous paragraph. Based on the content (ie value) of each entity, the macro can generate very different lines of code, or perhaps no code at all. After the evaluation phase of prepIX_I is complete, the resulting (generated) code is finalized by the prepIX_I preprocessor. Afterwards, this code never changes during the execution of the program.

Within the IX Family of Software, there is a Control file named "ix_eDict.txt". The values of the entities within this file control some of the main actions in the IX Software Family.

Invocation (Use) of a Function or an Entity or a Macro

A function and/or an entity and/or a macro can be invoked or used multiple times in a program. Each such use is called an invocation of the function, entity or macro. The flow of control (sometimes called the thread of execution) always passes through a function, an entity or a macro. Even when data is defined without executable statements, the flow of control will pass through these data-defining statements. A function or an entity or a macro can be mentioned (or used) zero, once or multiple times in a program or in another function or macro in the same program. Each such occurence is called an invocation of the function or entity or macro.

The use (invocation) of a function name that is not preceded by "def " always generates exactly one statement that is sometimes referred to as a function call or invocation. However, each invocation of a macro can generate zero, one or more statments.

Zero, one or more parameters can be passed into a function. An example of a function invocation that is passed exactly three parameters is shown in the example below:

	show("firstName", firstName, isShow)
	
This "show" function was created to result in the generation of a print statement used for debugging purposes. The statement(s) that will be run by the above "show" function will be:

	if isShow :    print("firstName: ", firstName, ":")
	
The variable "isShow" and "firstName" must have been defined prior to invoking the show function. A valid use of the show function can be seen below:

	isShow = True
	firstName = "Henry"
	show("firstName", firstName, isShow)
	
When appearing in a program, these statements will cause the equivalent of these statements to be run:

	isShow = True
	firstName = "Henry"
	if isShow :    print("firstName: ", firstName, ":")
	
These statements, when executed, will display on the screen:

	firstName: Henry:
	
This display informs the programmer of the current value of the variable "firstName" because isShow is True. If "isShow" is set to False before a show statement, the show statement will display nothing. During debugging, "isShow" is usually True. When debugging is complete, during use of the finished program, "isShow" is usually set to "False", causing nothing at all to be displayed by the show function. The function "show" is a very useful debugging tool, and it can even be left in the finished program, available to be used again if another debugging run is found to be necessary. To use it again, simply set "isShow=True". The author usually places a single "isShow=False" near the beginning of the program after removing all other "isShow =" statements from the final main program. There is an IX Control entity named "ix_isShow" that can be set to True (in the external control file ix_eDict.txt). The intention of the author is that this will override the value of isShow everywhere in the program.

Often a function will return one or more result(s). The results of a function are usually placed into variables by a statement that contains a single "=" character. Usually, there is only one result, but zero or any reasonable number of results are permitted. These results immediately follow the word "return" (prior to the line feed at the end of the return statement) within the function definition as shown below:

	return firstName
	
A macro causes statements to be generated, no variables are ever returned. An IX macro is never invoked in a statement immediately followed by an "=" character (although "=" signs can appear later in the statement that invokes a macro). However entities can be passed into an invoked macro in a similar but not identical manner to the passing of parameters into a function. A programmer begins the definition of a macro by assigning the macro a "macro file name" followed immediately by a "|". A macro definition is usually immediately followed by an invocation of this macro. The macro invocation can be followed by a "?" and an entity definition as shown below, but a preceding macro definition file-name must always be immediately followed by a "|". It is recommended that all IX macro names end with the string "_ix.py". An example of an IX macro invocation (being passed one entity definition) and immediately preceded by its macro definition is shown below:

	showIt_ix.py|
	show("&nam|",&nam|,isShow)
	showIt_ix.py?nam=firstName
	
If the correct macro definition is predefined, found and used by the preprocessor named prepIX, the macro invocation shown above will be used to generate the following line of code. The prepIX_I preprocessor will examine the definition of the macro named showIt_ix.py . Then prepIX_I will find and replace every occurance of the five characters "&nam|" with the character string "firstName" which is 9 characters long because the double quotes are not part of the search-string nor the replacement-string.

	show("firstName",firstName,isShow)
	
Once having been defined, either in the main program or external to the main program, the macro definition named showIt_ix.py can be invoked again. The following statement (immediately below) will be analyzed by prepIX:

	showIt_ix.py?nam=lastName
	
After analysis, prepIX_I will replace it by the (generated) single statement shown below:

	show("lastName",lastName,isShow)
	
Note that the "|" character is not needed when invoking a macro. Only a few keystrokes are economized by the use of a macro in the above example, but the macro concept can provide much efficiency for any programmer. Note that the name of the macro ("showIt_ix.py") must be stated when the macro is invoked, even if the macro is only defined in the main program. Furthermore, the macro name can be totally different from the actual characters that are defined in the macro definition (and hence those generated by prepIX.)

Note especially that the full file name (eg showIt_ix.py) of the macro definition must be used when invoking the macro, even if the macro does not appear in any folder nor package. The use of this name makes it possible to eventually house the macro definition external to the main program. Macros are often housed in the current folder or in an IX software package. Often macros are stored in the package named "ix_pkg_ix.py". The full list of IX_Control information can be found in

It is important to fully understand the difference between a macro definition and a function definition. The definition of a function must always occur (either using a "def" statment) or at least be mentioned in a "requires" or "import" statement before it is first invoked. The definition of a short macro does not normally contain the letters "def " and usually occurs immediately before the first time it is invoked. But a macro need not be defined in the program where it is invoked. Instead, the macro definition (especially a longer macro re-used by more than 1 main program) can be stored in the current folder where the main program is stored, or in a library of packaged modules. Names of IX macros must always end with "_ix.py". Non-macro names (of functions, programs or text files etc) should never end in "_ix.py".

Some Python functions can be invoked without being previously mentioned. These are called built-in Python functions. Examples are int() and exit(). Python permits access to external libraries containing functions. Such functions must be mentioned in an "import" statement before being invoked. The programmer is permitted to create IX functions that are stored in a folder or package. These functions must be mentioned in a "requires" statement in the program module before they are referenced (invoked). The prepIX_I preprocessor will search for all "required" functions in special folders external to the source code module. The prepIX_I preprocessor will also accept macro definitions defined within the main program or defined in folders outside the main program. The prepIX_I program will recognize an invocation of a macro by the suffix of the macro name which is "_ix.py" that is NOT immediately followed by a "|". Following "_ix.py" by a LF or EOL is recognized as an invocation of a macro.

	 WARNING - programmers must NOT code the string "_ix.py|" unless defining a macro
	
If a programmer absolutely needs to code "_ix.py|" when NOT defining a macro, it can usually be coded as "_ix.py"+"|". The prepIX_I preprocessor will recognize the end of a macro definition by the invocation of the macro, or by the string "&ix|" (followed by a LF) which indicates the end of a macro definition. The prepIX_I program searches for both external IX functions and for external IX macros using exactly the same search algorithms and search paths.

Do Macros Invoke Functions (or vice-versa?)

A macro can invoke one or more functions. But a function can also invoke one or more macros. This results in the age-old question: Which came first: the chicken or the egg? One can envisage a macro that generates a function definition or invocation; then this function can invoke a macro . . . ad infinitum. The IX Software does NOT allow this. The way that this is enforced is described next.

The IXp pre-processor is told to "process" an initial list of modules ending with the "main module". Any functions that contain macro invocations must appear in this list of modules. These modules are combined together into one basic set of concatenated code statements that contain no "generated" code. No macros will have been processed (yet) and no "requires" statements will have been used (yet) to cause the specified (required) functions to be included in the basic set of code statements. It is this basic set of code statements that will be used as input to the macro preprocessor. Any of the functions (or the main program) in the basic list of modules can contain entity and macro definitions and invocations. These entities and macros (in the basic list of concatenated modules) will be processed by the macro pre-processor. Any functions mentioned in "requires" statements will not be discovered (and will not yet be concatenated to prepend the basic set of statement) until after all macros have been fully processed.

This means that functions mentioned in "requires" statements must contain neither definitions nor invocations of any entities or macro. Such functions are usually the least complex functions. Any functions invoked by a macro must have either 1) appeared in the basic list of modules presented to the IXp preprocessor or 2) contain neither entity nor macro definitions. Any function modules that do not meet this condition must be included in the basic list of modules that is presented to IXp.

A complex main program is often subdivided into a number of subroutines or functions. Some of these parts (modules) of the complex main program might each need some macro-processing. Such parts of the complex program should be included in the initial (basic) list of modules that precede the complex main program module. Usually all entities and macros precede the complex program segments, functions or subroutines.

This may seem to lead to complexities that are impossible to resolve, but past experience has shown this to be quite simple to accomplish. Most common functions (found in packages or libraries) do NOT contain any macros. Such common functions can easily be mentioned in "requires" statements in the main program (or in segments of the main program that are listed in the initial basic list of modules.) Programmers having difficulty with this "chicken and egg" restriction should request assistance and/or advice from more experienced programmers.

If any entities or macros have not been resolved when automatic code generation is complete, they will result in error messages being thrown by prepIX_I or prepIX.

Searching For External Macros and Functions

Some IX macros can be defined external to the program where they are invoked. The prepIX_I preprocessor will search elsewhere for all macros that are invoked without being defined immediately before the first invocation. The prepIX_I preprocessor will search for such macro definitions in folders external to the main code module.

Whenever a program is being preprocessed or run through Python, a Command Line Interpreter (CLI) statement can be used to initiate this process. Such a CLI statement can accept the names of files containing code segments (modules) before the name of the main source code module (which must be the last module named). All external function definitions or external macro definitions appearing in the CLI statement will be concatenated to (before) the code in the main source code. An example of such a CLI statement is shown below:

>$ IXp -python3 showIt_ix.py show.py Test_show.py

This CLI statement will cause Python3 to evaluate, concatenate and run these three modules. This will cause the showIt_ix.py macro definition module and the show.py function definition module, containing the show() function definition, to prepend which means to be concatenated to (in front of) the Test_show.py module. To be found by Python, all three modules: the showIt_ix.py, show.py and Test_show.py modules are often located in the current folder. Unfortunately, the CLI command above will probably not function as expected because Python3 does NOT automatically invoke the prepIX_I preprocesor. This capability is part of the future plan for the IXp program. Perhaps the CLI statement shown below will operate correctly one day but not yet:


>$ IXc -prepIX_I showIt_ix.py show.py Test_show.py

Repositories of IX Functions (and IX Macros)

Functions are a very important, even critical, part of Python programming. Functions are a major part of the software created when using most if not all programming languages.

Macros are similar to Functions, but are not an essential part of Python. Macros can simpify programming tasks, but macros can be ignored and not used at all. Objects, which differ from variables, appear in many popular programming languages. Objects have been implemented in Python. These objects differ from variables; they are more powerful than variables. Special object methods can be defined; they are special functions that only work on objects. Object definitions can be imported in a similar manner to functions. Programs written to make major uses of objects are called Object-Oriented-Programs (OOP). The IX macros can be used with objects. An example of an Object that is often used on a Raspberry Pi is the GPIO group of objects that are used to reference and access GPIO pins. Objects will not be explained any further in this article.

In Raspberry Pi Python programming, there are at least 7 different repositories of functions:

   1. functions inherent to Python (approximately 80 built-in Python functions)
		e.g. exit(), int(), input(), len(), print(), range() etc
	All other functions must either be imported or defined within the main program code
	The statements defining these Python functions are never visible to the user.
   2. functions in external libraries (accessible to all users of Python)
	They must be "imported" into a module to be used.
	     e.g. import time, sys, GPIO
	e.g. time.sleep(1), sys.exit(), GPIO.output(4,1)
	 All imported functions are not visible in the main program code
  *3. in-line functions that accompany the source code of the main program
	These are defined by the user. Statements defining each of these
	functions appear as main program code and are visible to the user
  *4. functions stored in the current folder on the user's computer
	These functions are either written by or possessed by the user
	These statements are visible to the user
  *5. functions stored in user-accessible folders on the user's computer
	These functions are often written by or possessed by the user
	These statements are visible to the user
  *6. packages containing groups of functions
	groups of functions combined on a computer and often restricted
	to specific users.  Each Python system does this differently.
   7. functions stored in folders with user-defined paths
	Code "sys.path.append('folder')
	  to add a user-defined system path.
	Other system paths can also be defined.
A Raspberry Python programmer can add an additional repository folder by prefixing his main program code (for example) with the "sys.path.append" statement shown below. As shown, it would be appropriate to also include the statement to import the "requires" function when using Phase II of prepIX.
	import sys
	sys.path.append("/home/pi/Desktop/IX_assets/ix")
	#from ix_pkg import requires #(remove the first "#" for Phase II)
	#from ix_pymain_pkg import Test_show #(remove the leftmost "#" 
	#   to use main program code in ix_pymain_pkg.py)
	#/prepIn.py
	
Many, many of the author's small Python programs are stored in "Desktop/IX_assets/ix/ix_pymain_pkg.py". Most do not use version numbers. They have all been packed (stored) in the ix_pymain_pkg.py package to reduce clutter on any Raspberry boot drive, whether the boot drive is a microSD card or an SSD. As of 2023KNov08, the drive containing the most of the author's IX functions is the SABRENT 512GB SSD Raspberry drive. The fourth line above is a statement that illustrates how easy it is to access and run a single main Python program of the IX Family of Software.

A very simplified version of ix_pkg.py package can be seen in Source 07.

The IX Software Family only supports those repositories marked with an asterisk in the above list.

The following sample program makes full use of the features of Phase I of the IX Software Family. The program named "Test_show.py" shown below has been coded. It is stored in the IX package named "ix_pymain_pkg.py". The function pair, show.py and show_v00.py, have been stored in the package named "ix_pkg.py". The prepIX_I.py program has been placed in the sandBox folder (which is in the Desktop folder). The following Python statements are temporarily stored with the name "prepIn.py" and are read by prepIX_I from Linux stdin. The statements in prepIn.py are used to tell prepIX_I where to find the name of the program to be prepared. In this case, this name is, of course: "Test_show.py" :
	import sys
	sys.path.append("/home/pi/Desktop/IX_assets/ix")
	from ix_pkg import requires
	from ix_pymain_pkg import Test_show
	#prepIX_I will use Test_show.py to create main.py
	#/prepIn.py
	
This code named "prepIn.py" will be processed by prepIX_I, the resulting Python program will be stored in the sandBox folder with the name "main.py" . The user should then run this main.py program through Python. The user can do this using the following CLI statements in Terminal on the Raspberry Pi:

	>$ cd /home/pi/Desktop/IX_assets/sandBox
	>$ python prepIX_I.py <prepIn.py
	>$ python main.py
	
The prepIX_I program will generate (create) the final program named "main.py". When run by Python, it will display the following output:
	firstName: Henry:
	
To view the resulting valid Python program (that was generated by prepIX) named "main.py" that ran through Python, simply display it using "> $ more main.py" or "> $ cat main.py". Either one will display:
	>$ more main.py					
	import sys					(from Test_show.py)
	sys.path.append("/home/pi/Desktop/IX_assets/ix")  .. (ditto)

	from requires import requires			(from Test_show.py)
	#from ix_pkg retrieve show	      		(generated by prepIX_I as a comment)
	#   listing of show.py will follow		  ..
        def show( etc					(generated by prepIX)
	    . . . . 					  ..
	    return					  ..
	#from ix_pkg retrieve show_v00      		(generated by prepIX_I as a comment)
	#   listing of show_v00.py will follow   	  ..
        def show_v00( etc				(generated by prepIX)
	    . . . . 					  ..
	    return					  ..
	#from ix_pymain_pkg import Test_show		(from prepIn.py as a comment)
	requires("show(s,s,s,'v==0')")			(from Test_show.py)

	isShow = True					(from Test_show.py)
	firstName = "Henry"				(from Test_show.py)
	show("firstName",firstName,isShow)		(generated by prepIX)
	# ****LIST OF REQUIRED IX FUNCTION VERSIONS****	(generated by prepIX_I as a comment)
	# Highest     Loaded    Required	   MD5	  ..
	# Vsn Avail.  Vsn 	Vsn		   hash   ..
	# ----	      ----	------------------ ----	  ..
	# v00	      v00	show(s,s,s,'v==0') Pass	  ..
	# *********************************************	  ..
	#/ main.py      				(generated by prepIX_I as a comment)
	
At first glance, all of the above statements appear quite complex. But closer scrutiny makes us realise that the above code (in sandBox/main.py) contains only 2 function definitions and the 4 executable statments shown below:

	requires("show(s,s,s,'v==0')") 
	isShow = True
	firstName = "Henry"
	show("firstName",firstName,isShow)
	

The only reason for executing the first ("requires") of the 4 statements, is to make it possible for the "requires" statement to throw an error message, if necessary at run time. Successful programs that use the "requires" functionality will "silently" execute the "requires" statement. The word "silently" means "by not displaying any message". All original comments will have been suppressed by prepIX_I in order to reduce the size of the resulting "main.py" code. The prepIX_I program will have added all of the comment statements in the resulting "main.py" program shown above.

Upon reflection, the most important statement in this whole main.py program is:

	firstName = "Henry"
	
All of the rest of the code provides a rich environment that facilitates Python programming and debugging on a Raspberry Pi.

If the above functionality appeals to you, welcome to the IX Software Family. If you don't see anything above that is worth using, then the IX Software Family is probably not for you. Happy Python coding anyway !!!

The full list of components of the IX Software Family can be found in "ix_eDict.txt stored in Desktop/IX_assets/ix" on any of my Raspberry Pi computers.

Other Related Thoughts

Compute Module 4

(To enlarge .....Click it)
thumb: Raspberry-Pi-Introduces-New-Compute-Module-4.jpg
Raspberry Pi Compute Module 4


The author has recently (2023KNov02) purchased a Compute Module 4 with 8GM of Ram, 16GB of EMMC but no Wifi. It's datasheet can be seen in Source 02. A visual inspection of the CM4 does not find a silkscreen of the model identification which is CM4008016. The first "0" means no Wifi. Where can the model number be found on the actual device?

(To enlarge .....Click it)
thumb: CytronMakerCM4.jpg
Cytron CM4 Maker Board


 WARNING - Connecting the CM4 upside down will cause severe damage.
         (Heed the silk-screened outlines on the Maker board.)
The size of the Maker board (shown above) is much larger than the WaveShare board (shown below). The size difference is best understood by comparing the true-size shape outline of the CM4 compute module on each board. Of course, Cytron wanted to have many LEDs, many external device connectors, a few switches, an audio jack, a buzzer and a coin-battery for the RTC on their board (above) while WaveShare wanted to make their board's dimensions resemble an actual Raspberry Pi board (but with an SSD drive underneath) as shown below. Note that the CM4 compute module is mounted front to back (between its connector pair) on both boards.

See Source 04 for information on the Cytron CM4 Maker board (cost US$ 51.52 [+ shipping] bare without a CM4). What really caught my eye was the M.2 Key-M connector for the Sabrent 2230 SSD drive (mentioned in Source 05). This connector is not SATA SSD compatible. Of course, this Cytron board also has a Gigabyte Ethernet connector for CM4 boards lacking WiFi. Unfortunately, the CM4 Maker board could not provide any USB-3 connectors. Absence of USB-3 seems to be a difficult shortcoming of the CM4 to correct. The CM4 Maker board does have a microSD connector socket for those CM4 Lite boards (that don't have any eMMC memory). It can be powered by a USB-C connector or a 7v to 18v DC jack. A power switch is present. A RUN (Boot) switch selects booting from a microSD card instead of from an SSD drive (or the eMMC). The author intends to soon purchase a Cytron CM4 Maker board. CM4 connectors exist for a CSI Camera Port, a DSI Display Port and a coin-battery for the RTC. The CM4 Maker board also has 5 Grove connectors, 1 Stemma QT Quiic "Maker port", 10 GPIO leds, 3 push buttons, a full-size HDMI connector and a piezo buzzer with a mute switch. Source 06 is a group of lessons (projects) to help a user learn about the CM4 Maker Board.

It is very interesting to see that many of these features have been incorporated into the RPi 5. This speaks well of both devices!

(To enlarge .....Click it)
thumb: WaveShare_23228_C.jpg
WaveShare 23228C Board

[It was attempted to display both images (imm. above and above that) to the same scale].

PiShop US sells the WaveShare 23228 C, CM4-IO-BASE-C [version C is shown above] for US$ 25.95 bare without the CM4. This version C board has many, but not all of the features of the Cytron CM4 Maker Board. This WaveShare version C board is an upgrade of the version A board. This WaveShare version C board also has a M.2 Slot with PCIe M.2 KEY-M connector for a MVME SSD 2230 drive etc (located underneath). The webmaster has recently (as of 2023KNov20) ordered the WaveShare version C board. A WaveShare board (version A) can be purchased from Cytron as a kit containing everything needed (even including the CM4 compute module) to function like a normal Raspberry Pi Board, but which is configurable and faster. It will be very interesting to compare the 3 year old CM4 compute module with the RPi 5 (recently released in the fall of 2023).

Sources

Video Sources

Video Source V209:01: Introducing Raspberry Pi 5 (0:55m) by Raspberry Pi on 2023 I Sep 28

Web Sources

Web Source S209:01:www requires_v01_py.txt by D@CC on 2023JOct26
Web Source S209:02:www RPi Compute Module 4 DataSheet by Raspberry Pi c2023
Web Source S209:03:www RPi Compute Module 4 Review by Jeff Geerling on 2020JOct19
Web Source S209:04:www [Cytron] CM4 Maker Board by Cytron on 2023CMar08
Web Source S209:05:www Search for Sabrent 2230 in Article 206 by D@CC on 2023ISep25
Web Source S209:06:www . . . CM4 Maker Board [Lessons] by Abdul Salam A Haris at Cytron on 2023CMar08
Web Source S209:07:www ix_pkg_py.txt D@CC on 2023KNov03
Web Source S209:08:www ISO9003_Versions.txt D@CC on 2023KNov03
Web Source S209:09:www IX: IX Software Family: requires( function. . .) II (210.html) D@CC on 2023KNov07
Web Source S209:10:www IT: prepIX_I (196.html) D@CC on 2023EMay16
Web Source S209:11:www Pi: Creating Python Packages of Functions (174.html) D@CC on 2022BFeb26
Web Source S209:12:www WaveShare CM4-IO-BASE-C WaveShare after 2021JOct30

/SourcesEnd


There is a way to "google" any of the part-numbers, words or phrases in all my articles. This "google-like" search limits itself ONLY to my articles. Just go to the top of "Articles by Old King Cole" and look for the "search" input box named "freefind".

Click here to return to Articles by Old King Cole

Date Written :2023 K Nov 02
Last Updated:2023 K Nov 28

All rights reserved 2023 by © ICH180RR

saved in E:\E\2022\DevE\MyPagesE\Globat\ePhotoCaption.com\a\209\209.html
backed up to ePhotoCaption.com\a\209\209_2023KNov28.html

Font: Courier New 10 (monospaced)

/209.html